package gobase

import (
	"context"
	"os"
	"os/signal"
	"runtime/debug"
	"time"
)

var (
	GoFunCatchException bool = true
	OnCatchAllPanic     func(err interface{}, args ...interface{})

	// 处理输出的stacks
	onProcessPanicStackFunc = func(stacks []byte) []byte {
		return stacks
	}
)

//func GetCallStacks(skipCalls int) string {
//	return string(debug.Stack())
//}

// GetCallStacks 保留第一行goroutine信息，跳过指定次数的调用（每次调用对应2行）
// 优化点：单次循环完成定位，直接截取原始字节切片，无额外内存分配
func GetCallStacks(skip int) []byte {
	stack := debug.Stack()
	if len(stack) == 0 {
		return stack
	}

	skip += 2

	// 目标：保留[0, firstLineEnd] + [cutStart, len(stack)]
	var (
		firstLineEnd int        // 第一行结束索引（包含换行符）
		cutStart     int        // 跳过指定行后的起始索引
		state        int        // 0:寻找第一行结束 1:计数跳过的行
		newlineCount int        // 已跳过的行数（在state=1时有效）
		linesToSkip  = skip * 2 // 需要跳过的总行数
	)

	// 单次循环完成所有定位
	for i, b := range stack {
		if b != '\n' {
			continue
		}

		switch state {
		case 0:
			// 找到第一行结束位置
			firstLineEnd = i
			state = 1
			// 如果不需要跳过任何行，直接确定切割点
			if linesToSkip == 0 {
				cutStart = i + 1
				break
			}
		case 1:
			// 计数跳过的行
			newlineCount++
			if newlineCount == linesToSkip {
				cutStart = i + 1
				break // 找到目标后提前退出循环
			}
		}
	}

	// 处理边界情况
	switch {
	case cutStart == 0:
		// 未找到足够的行可跳过，仅返回第一行
		stack = stack[:firstLineEnd+1]
	default:
		// 拼接第一行和剩余有效部分（通过切片实现，无额外分配）
		stack = append(stack[:firstLineEnd+1], stack[cutStart:]...)
	}

	return onProcessPanicStackFunc(stack)
}

func ProcessStacks(stacks []byte) []byte {
	return onProcessPanicStackFunc(stacks)
}

func DeferCatchPanic(args ...interface{}) {
	if err := recover(); err != nil {
		if OnCatchAllPanic != nil {
			OnCatchAllPanic(err, args...)
		}
	}
}

func SetProcessPanicStackFunc(fn func(stacks []byte) []byte) {
	if fn == nil {
		fn = func(stacks []byte) []byte {
			return stacks
		}
	}
	onProcessPanicStackFunc = fn
}

func HookExitSignalAndInterval(runDuration time.Duration, interval time.Duration, intervalFn func()) int8 {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill)
	ti := time.NewTimer(interval)
	var runDurationCh <-chan time.Time
	if runDuration > 0 {
		runTicker := time.NewTicker(runDuration)
		runDurationCh = runTicker.C
	} else {
		runDurationCh = make(<-chan time.Time)
	}

	for {
		select {
		case <-c:
			return 0
		case <-runDurationCh:
			return 1
		case <-ti.C:
			intervalFn()
			ti.Reset(interval)
		}
	}
}

func RunInterval(runDuration time.Duration, interval time.Duration, intervalFn func()) {
	ti := time.NewTimer(interval)
	var runDurationCh <-chan time.Time
	if runDuration > 0 {
		runTicker := time.NewTicker(runDuration)
		runDurationCh = runTicker.C
	} else {
		runDurationCh = make(<-chan time.Time)
	}

	for {
		select {
		case <-runDurationCh:
			return
		case <-ti.C:
			intervalFn()
			ti.Reset(interval)
		}
	}
}

func RunIntervalEx(maxDuration time.Duration, interval time.Duration, intervalFn func() int) int {
	ti := time.NewTimer(interval)
	var runDurationCh <-chan time.Time
	if maxDuration > 0 {
		runTicker := time.NewTicker(maxDuration)
		runDurationCh = runTicker.C
	} else {
		runDurationCh = make(<-chan time.Time)
	}
	for {
		select {
		case <-runDurationCh:
			return 0
		case <-ti.C:
			ret := intervalFn()
			ti.Reset(interval)
			if ret != 0 {
				return ret
			}
		}
	}
}

func RunContextAndInterval(ctx context.Context, interval time.Duration, intervalFn func()) {
	ti := time.NewTimer(interval)
	for {
		select {
		case _ = <-ctx.Done():
			return
		case <-ti.C:
			intervalFn()
			ti.Reset(interval)
		}
	}
}
